/**
 * NGC相关工具类
 * @class ngc.component.ngc
 * @singleton
 */

define(function(c, s, f) {

	require(['frame/fish/locale/fish-desktop-locale.zh' ],function(){
		fish.setLanguage('zh');
	});
	// require([ 'frame/ext/contextMenu' ]);

	ngc = window.ngc = $.extend(fish,{
		'ngc_version' : '1.0.0',
		'language': 'zh',
		/**
		 * 获取上下文根，返回当前应用的Domain
		 * @method getContext
		 */
//		getContext : function() {
//			if (!this.webContext) {
//				var _webContext = document.location.href;
////				_webContext = _webContext.substring(
////						_webContext.indexOf('//') + 2, _webContext.length);
////				_webContext = _webContext.substring(
////						_webContext.indexOf('/') + 1, _webContext.length);
//				_webContext = _webContext
//						.substring(0, _webContext.indexOf('/'));
//				this.webContext = _webContext;
//			}
//			return this.webContext;
//		},
		
		getContext : function() {
			if (!this.webContext) {
				var _webContext = document.location.href;
				_webContext = _webContext.substring(_webContext.indexOf('//') + 2, _webContext.length);
				_webContext = _webContext.substring(0, _webContext.indexOf('/') + 1);
				this.webContext = "http://" + _webContext;
			}
			return this.webContext;
		},
		isFullscreenMode: function () {
            // we drop part of the criteria for fullscrenn mode identification
            // document.body.clientWidth == screen.width
            return document.body.clientHeight == screen.height;
        },
		extendObjectParam:function(data){
			var rst = {};
			$.each(data,function(key){
				if(!ngc.isFunction(this)){
					rst[key] = this;
				}
			})
		},
		registerServiceDataCache:function(fun){			
			if(fun&&ngc.isFunction(fun)){
				ngc._serviceCacheUsedFunction = fun;
			}
		},
		removeServiceDataCache:function(){
			ngc._serviceCacheUser = null;
		},
		resizePannelBody:function($pannel,size){
			if($pannel.children("div.panel-body").length>0&&size){
				if($pannel.children("div.panel-heading").length>0){
					size = size - 30;
				}
				if($pannel.children("div.panel-footer").length>0){
					size = size - ($pannel.children("div.panel-footer").outerHeight(true)>10?$pannel.children("div.panel-footer").outerHeight(true):10);
				}
				$pannel.children("div.panel-body").outerHeight(size);
			}
		},
		getPannelBodySize:function($pannel,size){
			if($pannel.children("div.panel-body").length>0&&size){
				if($pannel.children("div.panel-heading").length>0){
					size = size - 30;
				}
				if($pannel.children("div.panel-footer").length>0){
					size = size - ($pannel.children("div.panel-footer").outerHeight(true)>10?$pannel.children("div.panel-footer").outerHeight(true):10);
				}
				size = size-5
			}
			return size;
		},
		/**
		 * 启用/禁用覆盖层
		 * @method progress
		 * @param {String} [mess] 可选参数，遮罩层显示文字
		 */
		progress:function(mess){
			var $target = $(document.body);
			if ($target.data('full-screen')) {
                $.unblockUI();
                $target.data('full-screen', false);
            } else {
                $.blockUI({
                    message: mess||null
                });
                $target.data('full-screen', true);
            }
		},
		/**
		 * 根据字符串的unicode编码进行字符串长度的截取
		 * @method
		 * @param {String} source 原始字符串
		 * @param {Number} size 截取的长度
		 * @return {String} 截取后的字符串
		 */
		subStringByChar:function(source,size){
		    var len = -1;    
		    for (var i=0; i<source.length; i++) {    
		        if (source.charCodeAt(i)>127 || source.charCodeAt(i)==94) {    
		            len += 2;    
		        } else {    
		            len ++;    
		        }
		        if(len>=size){
		        	len = i;
		        	break ;
		        }
		     } 
		    
		    if(len!=-1){
		    	return source.substring(0,len+1);
		    }
		    return null;
		},
		/**
		 * 打开下载
		 * @method downLoad
		 * @param {String} url 下载的地址路径
		 * @param {Object} [params] 下载需要的数据，会串化传到后台,后台接收参数名downLoadData
		 */
		downLoad: function (url,params) {
			
			if(!url)return;
			
            var formSubmit = $("#_formDownLoad_CodeGenerate_id_1");

            var downLoadAction = url;

            if (formSubmit.length <= 0) {
                formSubmit = $("<form></form>");
                formSubmit.attr("action", downLoadAction);
                formSubmit.attr("method", "post");
                formSubmit.attr("target",
                    "_blank");
                formSubmit.attr("id", "_formDownLoad_CodeGenerate_id_1");
                formSubmit
                    .attr("name", "_formDownLoad_CodeGenerate_name_1");
                formSubmit.prependTo('body');
                formSubmit.css('display','none');
            }

            var data = $("#_formDownLoad_CodeGenerate_id_excelData");
            if (data.length <= 0) {
                data = $('<input type="hidden" name="downLoadData" id="_formDownLoad_CodeGenerate_id_excelData"/>');
                data.prependTo('#_formDownLoad_CodeGenerate_id_1');
            }
            data.val(JSON.stringify(params||''));           
            formSubmit.submit();
        },
		/**
		 * 调用服务端方法
		 * @method callServerFunction
		 * @param {String} interface 接口全限定名，比如:com.ztesoft.res.frame.userService
		 * @param {String} method 方法名，比如：queryUserDetail
		 * @param {Object} arg 接口参数，此处支持任意个参数，多个参数采用arg1,arg2,arg3...形式，参数支持基本数据类型、List（对应js的Array类型）、Map（对应js的Object类型），参数类型需要和服务端接口参数类型一致，否则会返回找不到目标方法异常。
		 * @param {Function} [success] 可选，成功之后回调函数，入参为后台接口方法返回的数据，当忽略该参数时候接口调用采用同步方式
		 * 
		 * <pre>
		 * 调用方式：ngc.callServerFunction("com.ztesoft.res.frame.userService","queryUserDetail",arg1,arg2,arg3...,function(data){console.log(data)});
		 * </pre>
		 */
		callServerFunction : function() {
			
			var arg = arguments;
			if (arg.length < 2) {
				return {
					mess : "执行服务器端方法错误，必须指定类名和方法名进行调用",
					resultStat : "ERROR"
				};
			}

			var params = [];
			var callBack, before, after, error;
			for (var i = 0; i < arg.length; i++) {
				if (typeof (arg[i]) == 'function') {
					if (!callBack) {
						callBack = arg[i];
					} else if (!before) {
						before = arg[i];
					} else if (!after) {
						after = arg[i];
					} else if (!error) {
						error = arg[i];
					}
				} else {
					if(typeof(arg[i])!='undefined')
						params.push(arg[i]);
				}

			}
			
			var cache = false;
			
			if(ngc._serviceCacheUsedFunction&&ngc.isFunction(ngc._serviceCacheUsedFunction)){
				if(ngc._serviceCacheUsedFunction.apply(this,arg)==true){					
					cache = true;					
				}				
			}
			if(cache){
				var data = ngc.store.get(JSON.stringify(params));
				
				if(data){
					if (callBack){
						var deffer = $.Deferred();
						callBack.apply(data, [ data.data?data.data:null ]);
						deffer.resolve(data); 
						return deffer.promise();
					}else{
						return data.data?data.data:null;
					}						
					
				}
			}
			
			var result;
			result = this.ajaxRequest({
				url : this.getContext()
						+ "callserverfunction.spr",
				obj : window,
				beforeLoad : function() {
					if (before)
						before.apply(this, [ arg ]);
				},
				loadErro : function(data) {
					if (error)
						return error.apply(data, [ data.data ]);					
				},
				afterLoad : function(data) {
					if (after)
						after.apply(data, [ data.data ]);					
				},
				params : {
					_callFunParams : JSON.stringify(params)
				},
				asyn : callBack ? true : false,
				paraseFunction : function(data) {
					if(cache){
						ngc.store.set(JSON.stringify(params),data)
					}
					if (callBack)
						callBack.apply(data, [ data.data ]);
					else
						result = data;
				}
			});
			return result;
		},
		addServiceExceptionHandel:function(fn){
			if(!ngc._handlerServiceExceptionStat){
				ngc._handlerServiceExceptionStat = [];
			}
			ngc._handlerServiceExceptionStat.push(fn);
		},
		ajaxRequest : function(reqParams) {
			reqParams = reqParams || {};
			var URI = reqParams.url;
			var beforeLoad = reqParams.beforeLoad;
			var paraseFunction = reqParams.paraseFunction || function() {
			};
			var obj = reqParams.obj || this;

			var afterLoad = reqParams.afterLoad;

			var loadErro = reqParams.loadErro;

			var asyn = reqParams.asyn || false;

			var params = reqParams.params || {};

			params._timeStamp = new Date().getTime();

			if (beforeLoad && typeof beforeLoad == "function") {
				beforeLoad.call(obj);
			}
			return fish.ajax({
				type : 'POST',
				url : URI,
				data : params,
				dataType : 'json',
				async : asyn,
				success : function(data) {
					_result = data;
					try {
						if (data.resultStat && data.resultStat != 'SUCCESS') {
							var mess = '服务端未知异常，请检查服务端代码，返回异常信息';
							if (data.mess) {
								mess = data.mess;
							}
							
							if(ngc._handlerServiceExceptionStat&&ngc.isArray(ngc._handlerServiceExceptionStat)){
								var rst = true;
								$.each(ngc._handlerServiceExceptionStat,function(i){
									rst = this(data);
									if(rst==false){
										return false;
									}
								});
								if(rst===false)return;
							}
							
							if (loadErro && typeof loadErro == "function") {
								var _r = loadErro.call(obj, data);
								if(_r===false)return;
							}
							
							
							fish.error({
								title : '调用服务异常',
								message : mess
							});
						} else {
							paraseFunction.call(obj, data);

						}
					} catch (e) {
						if (loadErro && typeof loadErro == "function") {
							loadErro.call(obj, data);
						}
						fish.error({
							title : '浏览器脚本错误',
							message : e.name + ": " + e.message + "<br>stack:"
									+ e.stack
						});
					}

					if (afterLoad && typeof afterLoad == "function") {
						afterLoad.call(obj, data);
					}
				},
				error : function(XMLHttpRequest, msg, error) {
					_result = {
						resultStat : "ERROR",
						mess : XMLHttpRequest.responseText,
						data : params,
						url : URI
					};
					if (loadErro && typeof loadErro == "function") {
						loadErro.call(obj, _result);
					}
					if (XMLHttpRequest.status == 404) {
						fish.error({
							title : 'Not Found Ajax Request:' + msg,
							message : url + ' ' + error
						});
					} else {
						fish.error({
							title : 'Ajax ' + msg,
							message : url + ' ' + error
						});
					}
				}
			});
		},
		/**
		 * js 强转int
		 */
		pasInt : function(str) {

			return parseInt(str);
		},
		/**
		 * js 强转String
		 */
		pasString : function(inStr) {

			return inStr + "";
		}

	});

	// fish = null;
	ngc.View.configure({
		manage : true
	});
	// 兼容IE8的bind
	if (!Function.prototype.bind) {
		Function.prototype.bind = function(oThis) {
			if (typeof this !== "function") {
				throw new TypeError(
						"Function.prototype.bind - what is trying to be bound is not callable");
			}
			var aArgs = Array.prototype.slice.call(arguments, 1), fToBind = this, fNOP = function() {
			}, fBound = function() {
				return fToBind.apply(this instanceof fNOP && oThis ? this
						: oThis, aArgs.concat(Array.prototype.slice
						.call(arguments)));
			};
			fNOP.prototype = this.prototype;
			fBound.prototype = new fNOP();
			return fBound;
		};
	}
	
	/**
	 * 根据树形结构，生成树形的加载顺序结构
	 * @method getTreeNodeSeq
	 * @param {Object} nodes 带有层次结构形如{id:"xxx",child:[{id:'222',child:[...]}]}这种数据结构，生成从顶端到底端顺序数组，广度优先遍历
	 * @param {Object} [childName] 树形子节点名称 
	 * @return {Array}  返回生成的数组
	 */
	ngc.getTreeNodeSeq = function(nodes,childName,pre){
		if(!childName){
			childName = 'child';
		}
		var rst = [];
		if(!pre){
			rst.push(nodes);
		}
		var _child = nodes[childName];
		if(_child){
			$.each(_child,function(){
				rst.push(this);
				if(this[childName]&&this[childName].length>0){
					$.merge(rst,ngc.getTreeNodeSeq(this,childName,true));
				}
			});
		}
		
		return rst;
	}
    /**
     * 弹出一个view，options中可以使用ngc.popup中options参数如modal，height等等
     * @member ngc.component.ngc
     * @method openView
     * @param {String | Object} options 为String或fish.View实例时相当于options.url；Object时可以包含以下属性：
     * @param {String} options.url 视图url，必选
     * @param {String} options.title 视图弹出标题，可选
     * @param {Function} options.close 对应popup.close方法
     * @param {Function} options.dismiss 对应popup.dismiss方法
     * @param {Function} options.callback(popup, view) 回调函数,popup：当前弹出窗；view当前视图
     * @param {Object} options.viewOption 子视图参数，如model，'collection', 'attributes', 'className'等，可选
     * @return {Object} 返回一个promise对象，参数为弹出的view的实例
     *<pre>
     *   ngc.openView('modules/user/userMgrView').then(function(view) {
     *      //view为弹出的视图实例
     *   });
     *   //or
     *   ngc.openView({
     *     url: 'modules/user/userMgrView',
     *     width: "60%"
     *    });
     *
     *   ngc.openView( new UserMgrView());
     *   //or
     *   ngc.openView({
     *     url:new UserMgrView(),
     *     width:'60%'
     *   });
     *   //or
     *   ngc.openView({
     *   url: UserMgrView,
     *   viewOption:{userId:10000},
     *   width:'60%'})
     *</pre>
     *
     */
	ngc.openView = function(options){
		 var popupPromise = $.Deferred();

	        if (ngc.isString(options) || (options instanceof ngc.View)) {
	            options = {url: options};
	        }

	        function renderPopupView(viewInstance) {
	            viewInstance.on('render', function () {
	                var $popupView, $el = viewInstance.$el, cloneOpts = $.extend({}, options);
	                delete cloneOpts.url;
	                delete cloneOpts.content;

	                $popupView = $el;
	                
	                if(!$el.hasClass('ui-dialog')){
	                	var template = '<div class="{{#if title}}ui-dialog dialog-lg{{/if}}">'
	            			+'{{#if title}}<div class="modal-header"><h5 class="modal-title">{{title}}</h5></div>{{/if}}'
	            			+'<div class="modal-body" id="{{id}}"></div></div>';
	                	var _tmpId = ngc.uniqueId('ui-dialog-content-');
	                	var tmp = ngc.compile(template);
	                	$popupView =$(tmp($.extend({id:_tmpId},options))).append($popupView);	                	
	                	viewInstance._obj_id = _tmpId;
	                }
	                var popOpts = $.extend({}, {content: $popupView}, cloneOpts); // 这里可以加入初始参数
	                //默认靠顶端
	                popOpts.position = popOpts.position||{};
	                
	                popOpts.position = $.extend({my: 'center center', at: "center center", of: window, collision:"fit" },popOpts.position);
	                
	                var popup = ngc.popup(popOpts);
	                viewInstance.popup = popup; //注入popup property，如果视图想自己关闭自己

	                var promise = popup.result;
	                options.close && fish.isFunction(options.close) && promise.then(options.close);
	                options.dismiss && fish.isFunction(options.dismiss) && promise.then(null, options.dismiss);

	                promise.always(function () {
	                    viewInstance.remove(); //移除body下的view
	                });

	            });

	            //TODO对于grid需要调整位置（style="height:300px"会出现重叠现象）
	            viewInstance.on('afterRender', function (view) {
	                view.$el.position(view.$el.data('position'));
	                if (fish.isFunction(options.callback)) {
	                    options.callback.call(window, view.popup, viewInstance);
	                }
	            });
	        }

	        if (ngc.isString(options.url)) {
	            require([options.url], function (View) {
	                var viewInstance = new View($.extend({}, options.viewOption));
	                // event order, beforeRender --> render --> afterRender  --> callback
	                renderPopupView(viewInstance);
	                viewInstance.render();
	                popupPromise.resolve(viewInstance);
	            });
	        } else {
	            //view 在页面中高度定制
	            var viewInstance;
	            if (options.url instanceof ngc.View) { 
	                viewInstance = options.url;
	            } else { 
	                var View = options.url;
	                viewInstance = new View($.extend({}, options.viewOption));
	            }
	            // event order, beforeRender --> render --> afterRender  --> callback
	            renderPopupView(viewInstance);
	            viewInstance.render();
	            popupPromise.resolve(viewInstance);
	        }

	        return popupPromise.promise();
	};
	$.extend($.ui.combobox.prototype,{
		 _text: function (text) {
	            if (text === undefined) {
	                if (this.options.editable) {
	                    return this.$input.val();
	                } else {
	                    return this.selected ? this.getSelectedItem()[this.options.dataTextField] : '';
	                }
	            } else {
	                if (this.options.editable) {
	                    this.$input.val(text);
	                } else {
	                    if (text === '') {
	                    	if(this.$display&&this.$placeholder)
	                    		this.$display.html(this.$placeholder);
	                    } else {
	                        var templateResult = this.options.templateResult;
	                        var resultText = templateResult ? templateResult(text) : text;
	                        this.$display.html(resultText);
	                    }
	                }
	            }
	        }
	});
	
	$.extend($.ui.switchbutton.prototype,{
		 _formSetValue: function (value) {
	            
	            if(value==0){
	            	this.element.prop("checked",false);
	            }else if(value==1){
	            	this.element.prop("checked",true);
	            }else if(value === true) {
	                this.element.prop("checked",true);
	            } else if (value === false){
	                this.element.prop("checked",false);
	            } else {
	                this.element.val(value);
	            }
	            this.element.trigger('change', true);
	        },
	     _formGetValue: function () {
	    	 if(this.option("state")==true)return '1';
	    	 if(this.option("state")==false)return '0';
	            return this.value();
	        }
		
	});
	
	$.extend($.ui.autocomplete.prototype,{
		_delegateEvent: function () {
	         var events = {
	             'click': '_onFocus',
	             'keydown': '_onKeydown'
	         };

	         if(!fish.browser.msie) {
	             events['blur'] = '_onBlur';
	         }
	         if (!fish.browser.msie || fish.browser.version > 9) {
	             events['input'] = '_onInput';
	             this._on(events);
	         } else {
	             // ie9 Doesn't fire an input event when deleting text (via Backspace, Delete, Cut, etc.).
	             // http://caniuse.com/#search=input
	             events['keyup'] = events['cut'] = events['paste'] = '_onSpecialInput';
	             this._on(events);

	             this._on({
	                 'keydown': '_onSpecialInput'
	             });
	         }

	         this._on(this.$list, {
	             'mousedown li': '_onMouseDown',
	             'click li': '_onClick'
	         });
	     }
	});
	 
	
	//扩展jqgrid的after insert事件位置
	$.extend($.ui.grid.prototype,{
		 addRowData: function(rowid, rdata, pos, src) {
	         var ts = this;
	         //如果rowid是对象,则说明参数没有rowid
	         if (typeof rowid === 'object') {
	             src = pos;
	             pos = rdata;
	             rdata = rowid;
	             rowid = undefined;
	         }
	         if (!pos) {
	             pos = "last";
	         }
	         var success = false,
	             cnm = ts.p.keyName,
	             nm, row, gi, ni, si, sind, data;
	         if (rdata) {
	             if (!$.isArray(rdata)) {
	                 if (rowid !== undefined) {
	                     rdata[cnm] = rowid; //万一用户新增的记录,传入了rowid,后面的data却没有这个字段
	                 }
	                 rdata = [rdata];
	             }
	             var datalen = rdata.length;
	             ni = ts.p.rownumbers === true ? 1 : 0;
	             gi = ts.p.multiselect === true ? 1 : 0;
	             si = ts.p.subGrid === true ? 1 : 0;

	             var k = 0,
	                 newsrc = src, //newsrc 在after的场景下,批量新增记录的时候,参考位置要变
	                 ids = [];
	             while (k < datalen) {
	                 data = rdata[k];
	                 rowid = data[cnm];
	                 if (rowid === undefined) {
	                     rowid = $.jgrid.randId();
	                     data[cnm] = rowid;
	                 }
	                 row = [];
	                 if (ni) {
	                     row[row.length] = ts.addRowNum(0, 0, 1, 0); //新增个0的,后续updatepager会更新的
	                 }
	                 if (gi) {
	                     row[row.length] = ts.addMulti(rowid, ni);
	                 }

	                 if (si) {
	                     row[row.length] = ts._addSubGridCell(rowid, ni);
	                 }

	                 for (i = gi + ni + si; i < ts.p.colModel.length; i++) {
	                     nm = ts.p.colModel[i].name;
	                     row[row.length] = ts.addCell(rowid, $.jgrid.getAccessor(data, nm), i, data);
	                 }
	                 row.unshift(ts._constructTr(rowid, data, false));
	                 row[row.length] = "</tr>";
	                 
	                 ids.push(rowid);
	                 
	                 if (ts.rows.length === 0) {
	                     $("table:first", ts.grid.bDiv).append(row.join(''));
	                 } else {
	                     switch (pos) {
	                         case 'last':
	                             $(ts.rows[ts.rows.length - 1]).after(row.join(''));
	                             break;
	                         case 'first':
	                             $(ts.rows[0]).after(row.join(''));
	                             break;
	                         case 'after':
	                             sind = this.getGridRowById(newsrc);
	                             if (sind) {
	                                 $(sind).after(row.join(''));
	                                 newsrc = rowid; //修复批量新增子节点顺序的问题
	                             }
	                             break;
	                         case 'before':
	                             sind = this.getGridRowById(src);
	                             if (sind) {
	                                 $(sind).before(row.join(''));
	                             }
	                             break;
	                     }
	                 }
	                 ts.p.records++;
	                 ts.p.reccount++;
	                 
	                 k++;
	             }

	             //新增节点的数据插入到对应的节点处,以免刷新的时候会换位置
	             var newdata = [];
	             if (pos === 'first') newdata = newdata.concat(rdata);
	             var parentindex = ts.p._index[src];
	             for (var i = 0; i < ts.p.data.length; i++) {
	                 if (pos === 'before') {
	                     if (parentindex == i) newdata = newdata.concat(rdata);
	                 }
	                 newdata.push(ts.p.data[i]);
	                 if (pos === 'after') {
	                     if (parentindex == i) newdata = newdata.concat(rdata);
	                 }
	             }
	             if (pos === 'last') newdata = newdata.concat(rdata);
	             ts.p.data = newdata;
	             ts.refreshIndex();

	             ts.updatepager(true, true);
	             success = true;
	             if ($(ts.grid.tDiv).html()) {
	                 $(ts.grid.tDiv).empty().removeClass('ui-jqgrid-tip');
	             }
	             
	             $.each(rdata,function(i){
	            	 ts._trigger("afterInsertRow", null, [ids[i], this]);
	             });
	         }
	         return success;
	     },
	     setCell: function(rowid, colname, nData, cssp, attrp, forceupd) {
	            var ts = this,
	                pos = -1,
	                v, title;
	            if (isNaN(colname)) {
	                $(ts.p.colModel).each(function(i) {
	                    if (this.name === colname) { //hidden属性需要考虑嘛?
	                        pos = i;
	                        return false;
	                    }
	                });
	            } else {
	                pos = parseInt(colname, 10);
	            }
	            if (pos >= 0) {
	                var ind = this.getGridRowById(rowid);
	                if (ind) {
	                    var tcell = $("td:eq(" + pos + ")", ind);
	                    if (nData !== "" || forceupd === true) {
	                        v = ts.formatter(rowid, nData, pos, ind, 'edit');
	                        title = ts.p.colModel[pos].title ? {
	                            "title": $.jgrid.stripHtml(v)
	                        } : {};
	                        if (ts.p.treeGrid && $(".tree-wrap", $(tcell)).length > 0) {
	                            $("span", $(tcell)).html(v).attr(title);
	                        } else {
	                            $(tcell).html(v).attr(title);
	                        }
	                        if (ts.p.datatype === "local") {
	                            var cm = ts.p.colModel[pos],
	                                index;
	                            nData = cm.formatter && typeof cm.formatter === 'string' && cm.formatter === 'date' ? $.unformat.date.call(ts, nData, cm) : 
	                            	( cm.specUnformat && typeof cm.specUnformat === 'function' ? cm.specUnformat.call(ts, nData, cm, tcell):nData);
	                            index = ts.p._index[rowid];
	                            if (index !== undefined) {
	                                ts.p.data[index][cm.name] = nData;
	                            }
	                        }
	                    }
	                    if (typeof cssp === 'string') {
	                        $(tcell).addClass(cssp);
	                    } else if (cssp) {
	                        $(tcell).css(cssp);
	                    }
	                    if (typeof attrp === 'object') {
	                        $(tcell).attr(attrp);
	                    }
	                }
	            }
	        },
        /**
         * 保存单元格
         * @param  {Number} irow 行索引
         * @param  {Number} icol 列索引
         */
        saveCell: function (irow, icol) {
            var ts = this, fr, rowid;
            if (!ts.grid || ts.p.cellEdit !== true) {
                return false;
            }
            if (ts.p.savedRow.length >= 1) {
                fr = 0;
            } else {
                fr = null;
            }
            if (fr !== null) {
                var cc = $("td:eq(" + icol + ")", ts.rows[irow]), v, v2, rowid = ts.rows[irow].id,
                    cm = ts.p.colModel[icol], nm = cm.name;
                var $selectCell = $(document.getElementById(rowid + "_" + nm), ts.rows[irow]);
                switch (cm.edittype) {
                    case "select":
                        if (!cm.editoptions.multiple) {
                            v = $selectCell.find("option:selected").val();
                            v2 = $selectCell.find("option:selected").text();
                        } else {
                            var sel = $("#" + rowid + "_" + nm, ts.rows[irow]), selectedText = [];
                            v = $(sel).val();
                            if (v) {
                                v = v.join(",");
                            } else {
                                v = "";
                            }
                            $("option:selected", sel).each(
                                function (i, selected) {
                                    selectedText[i] = $(selected).text();
                                }
                            );
                            v2 = selectedText.join(",");
                        }
                        if (cm.formatter) {
                            v2 = v;
                        }
                        break;
                    case "checkbox":
                        var cbv = ["Yes", "No"];
                        if (cm.editoptions) {
                            cbv = cm.editoptions.value.split(":");
                        }
                        v = $selectCell.is(":checked") ? cbv[0] : cbv[1];
                        v2 = v;
                        break;
                    case "password":
                    case "text":
                    case "textarea":
                    case "button" :
                        v = $selectCell.val();
                        v2 = v;
                        break;
                }
                // The common approach is if nothing changed do not do anything
                if (v2 !== ts.p.savedRow[fr].v) {

                    if (cm.editrules) {//add 校验逻辑
                        if ($.jgrid.validator.call(ts, ts.rows[irow].id, nm, cm.editrules) === false) {
                            return false;
                        }
                    }









                    if (ts._trigger("beforeSaveCell", null, [ts.rows[irow].id, nm, v, irow, icol]) === false) {
                        if (ts.p.cellAutoRestore) {
                            $(ts.element).jqGrid("restoreCell", irow, icol);
                        }
                        return false;
                    }

                    $(cc).empty();
                    $(ts.element).jqGrid("setCell", ts.rows[irow].id, icol, v2, false, false, true);
                    var leftpos = $(cc).position().left;
                    console.log("here");
                    $(cc).addClass("dirty-cell").prepend('<span class="span-dirty" style="margin-left:-5px"></span>');
                    $(ts.rows[irow]).addClass("edited");
                    ts._trigger("afterSaveCell", null, [ts.rows[irow].id, nm, v, irow, icol]);
                    ts.p.savedRow.splice(0, 1);
                    return true;

                } else {
                    $(ts.element).jqGrid("restoreCell", irow, icol);
                }
            }
            return true;

        },/**
         * 取消单元格编辑
         * @param  {Number} irow 行索引
         * @param  {Number} icol 列索引
         */
        restoreCell: function (irow, icol) {
            var ts = this, fr;
            if (!ts.grid || ts.p.cellEdit !== true) {
                return;
            }
            if (ts.p.savedRow.length >= 1) {
                fr = 0;
            } else {
                fr = null;
            }
            if (fr !== null) {

                if (ts._trigger("beforeRestoreCell", null, [ts.rows[irow].id, ts.p.savedRow[fr].v, irow, icol]) === false) {
                    return false;
                }

                var cc = $("td:eq(" + icol + ")", ts.rows[irow]);

                if ($.fn.validator) {//存在校验控件,清除出错信息
                    cc.resetValid();
                }

                $(cc).empty().attr("tabindex", "-1");
                $(ts.element).jqGrid("setCell", ts.rows[irow].id, icol, ts.p.savedRow[fr].v, false, false, true);
                var leftpos = $(cc).position().left;
                $(cc).hasClass("dirty-cell") ? $(cc).prepend('<span class="span-dirty" style="margin-left:-5px"></span>') : "";
                ts._trigger("afterRestoreCell", null, [ts.rows[irow].id, ts.p.savedRow[fr].v, irow, icol]);
                ts.p.savedRow.splice(0, 1);
            }
        }
	});

	
	
	//扩展checkbox是否选中
	 $.fieldValue = function (el, successful) {
	        var n = el.name,
	            t = el.type,
	            tag = el.tagName.toLowerCase();
	        if (successful === undefined) {
	            successful = true;
	        }
	        // || el.disabled 也取出来
	        if (successful && (!n || t == 'reset' || t == 'button' ||
	            (t == 'checkbox' || t == 'radio') && !el.checked ||
	            (t == 'submit' || t == 'image') && el.form && el.form.clk != el ||
	            tag == 'select' && el.selectedIndex == -1)) {
	        	if(t == 'checkbox')return false;
	            return null;
	        }

	        if (tag == 'select') {
	            var index = el.selectedIndex;
	            if (index < 0) {
	                return null;
	            }
	            var a = [];
	            var ops = el.options;
	            var one = (t == 'select-one');
	            var max = (one ? index + 1 : ops.length);
	            for (var i = (one ? index : 0); i < max; i++) {
	                var op = ops[i];
	                if (op.selected) {
	                    var v = op.value;
	                    if (!v) { // extra pain for IE...
	                        v = (op.attributes && op.attributes.value && !(op.attributes.value.specified)) ? op.text : op.value;
	                    }
	                    if (one) {
	                        return v;
	                    }
	                    a.push(v);
	                }
	            }
	            return a;
	        }
	        return $(el).val();
	    };
	    
	    
	    /*ngc.ResizeView = ngc.View.extend({
	    	resize: function(delta) {				
				ngc.each(this.__manager__.views, function (views) {
					ngc.each($.makeArray(views), function (view) {
						if (view.$el.is(':visible')) {
							if (ngc.isFunction(view.resize)) {
								var delta = view.$el.parent().height() - view.$el.outerHeight(true);
								view.resize(delta);
							}
						}
					}, this);
				});
	    	}
	    });
	    
	    ngc.View = ngc.ResizeView;*/

	/**
	 * @method setLanguage 赋值语言，内部会去更新组件相应资源内容
	 * @param language
	 *            语言
	 */

	/**
	 * 封装后的ajax方法,提供默认的遮罩和错误提示，局部遮罩可自行实现。
	 * 
	 * @method ajax
	 * @param {Object}  options ngc.ajax支持原生的所有ajax参数及回调方法
	 *           
	 * 
	 * 扩展了以下几个参数：
	 * 
	 * @param {Function} [options.beforeSend] function(xhr,status){//在发送请求之前要做的事情...}
	 *            
	 * @param {Function} [options.complete] function(xhr,status){//加载完成之后要做的事情...}
	 *        
	 * @param {Function} [options.error] function(xhr){//出现异常的时候要做的事情...}
	 *            
	 * @param {Boolean} [options.showMask=false] 是否采用默认的全局遮罩。
	 *            
	 * @param {Boolean} [options.showError=true] 采用默认的错误提示。
	 *            
	 * @return {Object} 返回原生ajax对象,支持promise等方式调用
	 */

	/**
	 * 提供方法的延迟操作,产生全局遮罩.一般用于初始化页面;初始化方法执行之前加上遮罩,结束之后取消遮罩
	 * 
	 * @method wait
	 * @param {Function} func 要执行的函数,执行期间会参数全局遮罩
	 *            
	 * @return {Object} 要执行的函数的返回值
	 */


	/**
	 * 获取对象的属性值,深层取值
	 * @method getAccessor
	 * @param {Object} obj 目标对象
	 *           
	 * @param {String} expr 对象的属性值,支持级联获取到最底层
	 *            
	 * @return {Object} 目标值
	 */

	/**
	 * 获取垂直滚动条宽度
	 * @method getScrollBarWidth 
	 * @return {Number} 返回滚动条宽度
	 */

	/**
	 * 获取元素的widget实例
	 * @method widgetInstance 
	 * @param {Object} $el jQuery元素
	 *           
	 * @returns {*}
	 */

	/**
	 * 生成唯一的id标识
	 * @method getUUID 
	 * @return {String} res 生成的id
	 */

	/**
	 * @method substitute 字符串替换
	 * @param {String} str 要格式化的字符串
	 *           
	 * @param {string} args 替换后的内容
	 *           
	 * 
	 * <pre>
	 * ngc.substitute('{0}, {1}', 'a', 'b'); //'a, b'
	 * </pre>
	 */

	/**
	 * @method on 全局事件监听。 在对象上绑定一个callback回调函数。只要event触发，该回调函数就会调用。
	 *         如果你的页面含有大量的不同事件，我们约定使用冒号来为事件添加命名空间，也可以同时绑定多个事件，事件用空格分隔。
	 *         如果监听的是"all"事件，那么任何事件的触发都会触发该回调函数，回调函数的第一个参数会传递该事件的名称。
	 * @param event
	 *            事件名称
	 * @param callback
	 *            回调函数
	 * @param [context]
	 *            给回调函数提供一个上下文，默认以该对象为函数上下文
	 * 
	 * <pre>
	 * ngc.on(&quot;change&quot;, this.change(), this);
	 * </pre>
	 * <pre>
	 *      ngc.on(&quot;change:title change:author&quot;, ...);
	 * </pre>
	 * <pre>
	 * ngc.on(&quot;all&quot;, function(eventName) {
	 * 	console.log(eventName);
	 * });
	 * </pre>
	 */

	/**
	 * @method off 移除全局事件监听。 移除对象先前绑定的callback函数。
	 *         如果没有指定context（上下文），所有上下文下的这个回调函数将被移除。
	 *         如果没有指定callback，所有绑定这个事件回调函数将被移除。 如果没有指定event，所有事件的回调函数会被移除。
	 * @param [event]
	 *            事件名称
	 * @param [callback]
	 *            回调函数
	 * @param [context]
	 *            回调函数的上下文，默认以该对象为函数上下文
	 * 
	 * <pre>
	 * // Removes just the `onChange` callback.
	 * ngc.off(&quot;change&quot;, onChange);
	 * 
	 * // Removes all &quot;change&quot; callbacks.
	 * ngc.off(&quot;change&quot;);
	 * 
	 * // Removes the `onChange` callback for all events.
	 * ngc.off(null, onChange);
	 * 
	 * // Removes all callbacks for `context` for all events.
	 * ngc.off(null, null, context);
	 * 
	 * // Removes all callbacks on `fish`.
	 * ngc.off();
	 * </pre>
	 */
	
	   /**
     * @method success
     * 成功提示
     *
     * @param {Object|String} message 如果传入的是一个对象，请使用{title:'标题',message:'内容'}这种格式，或者直接{message:'内容'}。
     * 如果传入的是一个字符串，将会直接作为消息显示出来。
     * @param {Function} [closeFunc] 关闭处理函数
     *
     * @returns {{result: *, close: Function, dismiss: Function}}
     * @returns {Object} return.result 返回结果Promise对象，popup.result.then(function resolve(){}, function reject(){});
     * @returns {Function} return.close 确认关闭窗口函数，popup.close(result)
     * @returns {Function} return.dismiss 取消关闭窗口函数，popup.dismiss(reason)
     */
    
    /**
     * @method require
     * require方法挂在ngc这个全局单例静态对象上
     */
    /**
     * @method define
     * define方法挂在ngc这个全局单例静态对象上
     */

    /**
     * @method info
     * 信息提示
     *
     * @param {Object|String} message 如果传入的是一个对象，请使用{title:'标题',message:'内容'}这种格式，或者直接{message:'内容'}。
     * 如果传入的是一个字符串，将会直接作为消息显示出来。
     * @param {Function} [closeFunc] 关闭处理函数
     *
     * @returns {{result: *, close: Function, dismiss: Function}}
     * @returns {Object} return.result 返回结果Promise对象，popup.result.then(function resolve(){}, function reject(){});
     * @returns {Function} return.close 确认关闭窗口函数，popup.close(result)
     * @returns {Function} return.dismiss 取消关闭窗口函数，popup.dismiss(reason)
     */
  

    /**
     * @method warn
     * 告警提示
     *
     * @param {Object|String} message 如果传入的是一个对象，请使用{title:'标题',message:'内容'}这种格式，或者直接{message:'内容'}。
     * 如果传入的是一个字符串，将会直接作为消息显示出来。
     * @param {Function} [closeFunc] 关闭处理函数
     *
     * @returns {{result: *, close: Function, dismiss: Function}}
     * @returns {Object} return.result 返回结果Promise对象，popup.result.then(function resolve(){}, function reject(){});
     * @returns {Function} return.close 确认关闭窗口函数，popup.close(result)
     * @returns {Function} return.dismiss 取消关闭窗口函数，popup.dismiss(reason)
     */
   

    /**
     * @method error
     * 错误提示
     *
     * @param {Object|String} message 如果传入的是一个对象，请使用{title:'标题',message:'内容'}这种格式，或者直接{message:'内容'}。
     * 如果传入的是一个字符串，将会直接作为消息显示出来。
     * @param {Function} [closeFunc] 关闭处理函数
     *
     * @returns {{result: *, close: Function, dismiss: Function}}
     * @returns {Object} return.result 返回结果Promise对象，popup.result.then(function resolve(){}, function reject(){});
     * @returns {Function} return.close 确认关闭窗口函数，popup.close(result)
     * @returns {Function} return.dismiss 取消关闭窗口函数，popup.dismiss(reason)
     */
   

    /**
     * @method confirm
     * 确认提示
     *
     * @param {Object|String} message
     * 如果传入的是一个字符串，将会直接作为消息显示出来。
     * 如果传入的是一个对象，对象结构{title:'标题',message:'内容', ok: 'Yes', cancel: 'No'}这种格式
     * @param {Function} [okFunc] 确认处理函数
     * @param {Function} [cancelFunc] 取消处理函数
     *
     * @returns {{result: *, close: Function, dismiss: Function}}
     * @returns {Object} return.result 返回结果Promise对象，popup.result.then(function resolve(){}, function reject(){});
     * @returns {Function} return.close 确认关闭窗口函数，popup.close(result)
     * @returns {Function} return.dismiss 取消关闭窗口函数，popup.dismiss(reason)
     */
   

    /**
     * @method prompt
     * 输入提示
     *
     * @param {Object|String} arg 如果传入的是一个对象，请使用{title:'标题',message:'内容'}这种格式，或者直接{message:'内容'}。
     * 如果传入的是一个字符串，将会直接作为消息显示出来。
     * @param {String} value 输入框默认内容 可选
     *
     * @returns {{result: *, close: Function, dismiss: Function}}
     * @returns {Object} return.result 返回结果Promise对象，popup.result.then(function resolve(){}, function reject(){});
     * @returns {Function} return.close 确认关闭窗口函数，popup.close(result)
     * @returns {Function} return.dismiss 取消关闭窗口函数，popup.dismiss(reason)
     */
   

    /**
     * @method blockedConfirm 阻塞提示（用于书写同步代码），结合事件使用
     * @param {Event} e 事件对象
     * @param {String} message 提示信息
     * @returns {Boolean} 返回是否阻塞，true不阻塞，false阻塞
     */
	


    /**
     * 遍历obj，按顺序遍历输出每个值。Aliased as `forEach`。如果存在原生的forEach方法，Underscore就使用它代替。
     * The cornerstone, an `each` implementation, aka `forEach`.
     * Handles objects with the built-in `forEach`, arrays, and raw objects.
     * Delegates to **ECMAScript 5**'s native `forEach` if available.
     * @method each
     * <pre>
     ngc.each([1, 2, 3], alert);
     => alerts each number in turn...
     ngc.each({one: 1, two: 2, three: 3}, alert);
     => alerts each number value in turn...
     * </pre>
     * @param obj 可以是数组也可以是对象
     * @param {Function} iterator 用来处理每个元素.如果obj是数组,参数为(element, index, obj);如果obj是个对象,iterator的参数是 (value, key, obj)
     * @param context (optional) 如果传递了context参数,则把iterator绑定到context对象上
     */

    /**
     * 遍历obj，按顺序映射每个值到一个新的数组中。Aliased as `collect`.如果存在原生的map方法，就用原生map方法来代替。
     * Return the results of applying the iterator to each element.
     * Delegates to **ECMAScript 5**'s native `map` if available.
     * @method map
     * <pre>
     ngc.map([1, 2, 3], function(num){ return num * 3; });
     => [3, 6, 9]
     ngc.map({one: 1, two: 2, three: 3}, function(num, key){ return num * 3; });
     => [3, 6, 9]
     * </pre>
     * @param obj 可以是数组也可以是对象
     * @param {Function} iterator 用来处理每个元素并返回。如果obj是数组，参数为(element, index, obj);如果obj是个对象，iterator的参数是 (value, key, obj)
     * @param context (optional)  如果传递了context参数，则把iterator绑定到context对象上
     * @return {Array} 返回一个新的数组
     */

    /**
     * 遍历obj，按顺序将每个值归结为一个单独的值。Aliased as `foldl/inject`。
     * **Reduce** builds up a single result from a list of values, aka `inject`,
     * or `foldl`. Delegates to **ECMAScript 5**'s native `reduce` if available.
     * @method reduce
     * <pre>
     var sum = ngc.reduce([1, 2, 3], function(memo, num){ return memo + num; }, 0);
     => 6
     * </pre>
     * @param obj 可以是数组也可以是对象
     * @param {Function} iterator 用来处理每个元素并返回。如果obj是数组，参数为(memo, element, index, obj);如果obj是个对象，iterator的参数是 (memo, value, key, obj)
     * @param memo 函数的初始值，后续每一步值都需要由iterator返回
     * @param context (optional) 如果传递了context参数，则把iterator绑定到context对象上
     * @return 返回一个新的值
     */

    /**
     * 遍历obj，按倒顺将每个值归结为一个单独的值。Aliased as `foldr`。
     * The right-associative version of reduce, also known as `foldr`.
     * Delegates to **ECMAScript 5**'s native `reduceRight` if available.
     * @method reduceRight
     * <pre>
     var list = [[0, 1], [2, 3], [4, 5]];
     var flat = ngc.reduceRight(list, function(a, b) { return a.concat(b); }, []);
     => [4, 5, 2, 3, 0, 1]
     * </pre>
     * @param  {Object} obj 可以是数组也可以是对象
     * @param  {Function} iterator 用来处理每个元素并返回。如果obj是数组，参数为(memo, element, index, obj);如果obj是个对象，iterator的参数是 (memo, value, key, obj)
     * @param  {Object} memo 函数的初始值，后续每一步值都需要由iterator返回
     * @param  {Object} context (optional) 如果传递了context参数，则把iterator绑定到context对象上
     * @return {Object} 返回一个新的值
     */

    /**
     * 遍历obj，返回第一个通过predicate迭代器真值检测的元素值。Aliased as `detect`。如果找到匹配的元素，函数将立即返回，不会遍历整个obj。
     * Return the first value which passes a truth test. Aliased as `detect`.
     * @method find
     * <pre>
     var even = ngc.find([1, 2, 3, 4, 5, 6], function(num){ return num % 2 == 0; });
     => 2
     * </pre>
     * @param  {Object} obj 一般为数组
     * @param  {Function} predicate 参数为(memo, element, index, obj)，用来匹配符合条件的值
     * @param  {Object} context (optional) 如果传递了context参数，则把iterator绑定到context对象上
     * @return {Object} 返回第一个匹配的值
     */

    /**
     * 遍历list中的每个值，返回包含所有通过iterator真值检测的元素值。Aliased as `select`。如果存在原生filter方法，则用原生的filter方法。
     * Return all the elements that pass a truth test.
     * Delegates to **ECMAScript 5**'s native `filter` if available.
     * Aliased as `select`.
     * @method filter
     * <pre>
     var evens = ngc.filter([1, 2, 3, 4, 5, 6], function(num){ return num % 2 == 0; });
     => [2, 4, 6]
     * </pre>
     * @param  {Object} list 一般为数组
     * @param  {Function} iterator 参数为(memo, element, index, obj)，用来匹配符合条件的值
     * @param  {Object} context (optional) 如果传递了context参数，则把iterator绑定到context对象上
     * @return {Array} 返回符合条件的数据集合
     */

    /**
     * 返回list中没有通过iterator真值检测的元素集合，与filter相反。
     * Return all the elements for which a truth test fails.
     * @method reject
     * <pre>
     var odds = ngc.reject([1, 2, 3, 4, 5, 6], function(num){ return num % 2 == 0; });
     => [1, 3, 5]
     * </pre>
     * @param  {Object} list 一般为数组
     * @param  {Function} iterator 参数为(memo, element, index, obj)，用来过滤掉符合条件的值
     * @param  {Object} context (optional) 如果传递了context参数，则把iterator绑定到context对象上
     * @return {Array} 返回不符合条件的数据集合
     */

    /**
     * 如果list中的所有元素都通过iterator的真值检测就返回true。Aliased as `all`。如果存在原生的every方法，就使用原生的every。
     * Determine whether all of the elements match a truth test.
     * Delegates to **ECMAScript 5**'s native `every` if available.
     * Aliased as `all`.
     * @method every
     * <pre>
     ngc.every([true, 1, null, 'yes']);
     => false
     * </pre>
     * @param  {Object} list 一般为数组
     * @param  {Function} iterator (optional)  可为空，参数为(memo, element, index, obj)，用来匹配符合条件的值,为空的话就默认为**{@link ngc.component.ngc#identity identity}**方法
     * @param  {Object} context (optional)  如果传递了context参数，则把iterator绑定到context对象上
     * @return {Boolean} 如果数据都匹配返回true，否则返回fasle
     */

    /**
     * 如果list中有任何一个元素通过 iterator 的真值检测就返回true。Aliased as `some`。一旦找到了符合条件的元素, 就直接中断对list的遍历. 如果存在原生的some方法，就使用原生的some。
     * Determine if at least one element in the object matches a truth test.
     * Delegates to **ECMAScript 5**'s native `some` if available.
     * Aliased as `any`.
     * @method some
     * <pre>
     ngc.some([null, 0, 'yes', false]);
     => true
     * </pre>
     * @param  {Object} list 一般为数组
     * @param  {Function} iterator (optional) 可为空，参数为(memo, element, index, obj)，用来匹配符合条件的值,为空的话就默认为**{@link ngc.component.ngc#identity identity}**方法
     * @param  {Object} context (optional) 如果传递了context参数，则把iterator绑定到context对象上
     * @return {Boolean} 如果有任一数据匹配返回true，否则返回fasle
     */

    /**
     * 如果obj包含指定的value则返回true。Aliased as `include`。如果list 是数组，内部使用indexOf判断
     * Determine if the array or object contains a given value (using `===`).
     * Aliased as `include`.
     * @method contains
     * <pre>
     ngc.contains([1, 2, 3], 3);
     => true
     * </pre>
     * @param  {Object} obj 一般为数组
     * @param  {Object} value 需要判断的指定值
     * @return {Boolean} 如果对象内包含指定值返回true，否则返回fasle
     */

    /**
     * 在list的每个元素上执行method方法。任何传递给invoke的额外参数，invoke都会在调用methodName方法的时候传递给它。
     * Invoke a method (with arguments) on every item in a collection.
     * @method invoke
     * <pre>
     ngc.invoke([[5, 1, 7], [3, 2, 1]], 'sort');
     => [[1, 5, 7], [1, 2, 3]]
     * </pre>
     * @param  {Object} list 一般为数组
     * @param  {Function} method 每一个元素要执行的方法,也可以是方法名称
     * @return {Array} 返回一个新的数组
     */

    /**
     * map最常使用的用例模型的版本，即萃取对象数组中某属性值，返回一个数组。
     * Convenience version of a common use case of `map`: fetching a property.
     * @method pluck
     * <pre>
     var stooges = [{name: 'moe', age: 40}, {name: 'larry', age: 50}, {name: 'curly', age: 60}];
     ngc.pluck(stooges, 'name');
     => ["moe", "larry", "curly"]
     * </pre>
     * @param  {Object} obj 一般为对象数组
     * @param  {Object} key 要萃取的对象的属性名称
     * @return {Array} 返回一个新的数组
     */

    /**
     * 遍历list中的每一个值，返回一个数组，这个数组包含包含properties所列出的属性的所有的键 - 值对。
     * Convenience version of a common use case of `filter`: selecting only objects
     * containing specific `key:value` pairs.
     * @method where
     * <pre>
     ngc.where(listOfPlays, {author: "Shakespeare", year: 1611});
     => [{title: "Cymbeline", author: "Shakespeare", year: 1611},
     {title: "The Tempest", author: "Shakespeare", year: 1611}]
     * </pre>
     * @param list 一般为对象数组
     * @param properties 需要过滤的属性集合
     * @return {Array} 返回一个新的数组
     */

    /**
     * 遍历list中的每一个值，返回匹配properties所列出的属性的所有的键 - 值对的第一个值。
     * Convenience version of a common use case of `find`: getting the first object
     * containing specific `key:value` pairs.
     * @method findWhere
     * <pre>
     ngc.findWhere(publicServicePulitzers, {newsroom: "The New York Times"});
     => {year: 1918, newsroom: "The New York Times",
      reason: "For its public service in publishing in full so many official reports,
      documents and speeches by European statesmen relating to the progress and
      conduct of the war."}
     * </pre>
     * @param list 一般为对象数组
     * @param properties 需要过滤的属性集合
     * @return {Array} 返回第一个符合条件的对象
     */

    /**
     * 返回list中的最大值。如果传递iterator参数，iterator将作为list排序的依据。如果是数字数组(不超过65535个)，直接调用Math.max方法。
     * Return the maximum element or (element-based computation).
     * Can't optimize arrays of integers longer than 65,535 elements.
     * See [WebKit Bug 80797](https://bugs.webkit.org/show_bug.cgi?id=80797)
     * @method max
     * <pre>
     var stooges = [{name: 'moe', age: 40}, {name: 'larry', age: 50}, {name: 'curly', age: 60}];
     ngc.max(stooges, function(stooge){ return stooge.age; });
     => {name: 'curly', age: 60};
     * </pre>
     * @param  list 一般为数组
     * @param  {Function} iterator (optional) 返回每一个数据中用来比较的值，如果未定义，默认为其本身。
     * @param  {Object} context (optional) 如果传递了context参数，则把iterator绑定到context对象上
     * @return {Object} 返回经比较后最大的数据
     */

    /**
     * 返回list中的最小值。如果传递iterator参数，iterator将作为list排序的依据。
     * Return the minimum element (or element-based computation).
     * @method min
     * <pre>
     var numbers = [10, 5, 100, 2, 1000];
     ngc.min(numbers);
     => 2
     * </pre>
     * @param  list 一般为数组
     * @param  {Function} iterator (optional) 返回每一个数据中用来比较的值，如果未定义，默认为其本身。
     * @param  {Object} context (optional) 如果传递了context参数，则把iterator绑定到context对象上
     * @return {Object} 返回经比较后最小的数据
     */

    /**
     * 返回一个随机乱序的 obj 副本, 使用[Fisher-Yates shuffle](http://en.wikipedia.org/wiki/Fisher–Yates_shuffle)来进行随机乱序.
     * Shuffle an array, using the modern version of the
     * [Fisher-Yates shuffle](http://en.wikipedia.org/wiki/Fisher–Yates_shuffle).
     * @method shuffle
     * <pre>
     ngc.shuffle([1, 2, 3, 4, 5, 6]);
     => [4, 1, 6, 3, 5, 2]
     * </pre>
     * @param  {Array} obj 一般为数组
     * @return {Array} 返回乱序的数组
     */

    /**
     * 从 obj中产生一个随机样本。传递一个数字表示从obj中返回n个随机元素。否则将返回一个单一的随机项。
     * Sample **n** random values from a collection.
     * If **n** is not specified, returns a single random element.
     * The internal `guard` argument allows it to work with `map`.
     * @method sample
     * <pre>
     ngc.sample([1, 2, 3, 4, 5, 6]);
     => 4
     ngc.sample([1, 2, 3, 4, 5, 6], 3);
     => [1, 6, 2]
     * </pre>
     * @param  {Array} obj 一般为数组
     * @param  {Number} n (optional) 随机返回的元素个数
     * @param  {Object} guard (optional) 设置为true会返回单一的随机项
     * @return {Object} 如果第二参数未定义或者定义了第三个参数，则返回随机一项，其他适合则返回n个随机元素的集合
     */

    /**
     * 返回一个排序后的obj拷贝副本。
     * Sort the object's values by a criterion produced by an iterator.
     * @method sortBy
     * <pre>
     ngc.sortBy([1, 2, 3, 4, 5, 6], function(num){ return Math.sin(num); });
     => [5, 4, 6, 3, 1, 2]
     * </pre>
     * @param  {Array} obj 一般为数组
     * @param  {Function} iterator (optional) 如果有iterator参数，iterator将作为obj排序的依据。迭代器也可以是字符串的属性的名称进行排序的
     * @param  {Object} context (optional) 如果传递了context参数，则把iterator绑定到context对象上
     * @return {Array} 返回乱序的数组
     */

    /**
     * 把一个集合分组为多个集合，通过 iterator 返回的结果进行分组.
     * Groups the object's values by a criterion. Pass either a string attribute to group by, or a function that returns the criterion.
     * @method groupBy
     * <pre>
     ngc.groupBy([1.3, 2.1, 2.4], function(num){ return Math.floor(num); });
     => {1: [1.3], 2: [2.1, 2.4]}
     ngc.groupBy(['one', 'two', 'three'], 'length');
     => {3: ["one", "two"], 5: ["three"]}
     * </pre>
     * @param  {Array} obj 一般为数组
     * @param  {Function} iterator (optional) 用来返回一个在列表中的每个元素键的函数或属性名，返回的值作为key。
     * @param  {Object} context (optional) 如果传递了context参数，则把iterator绑定到context对象上
     * @return {Object} 返回分组的对象
     */

    /**
     * 给定一个list，和一个用来返回一个在列表中的每个元素键的iterator函数（或属性名），返回一个每一项索引的对象。
     * Indexes the object's values by a criterion, similar to `groupBy`, but for when you know that your index values will be unique.
     * @method indexBy
     * <pre>
     var stooges = [{name: 'moe', age: 40}, {name: 'larry', age: 50}, {name: 'curly', age: 60}];
     ngc.indexBy(stooges, 'age');
     => {
          "40": {name: 'moe', age: 40},
          "50": {name: 'larry', age: 50},
          "60": {name: 'curly', age: 60}
        }
     * </pre>
     * @param  {Array} list 一般为数组
     * @param  {Function} iterator (optional) 用来返回一个在列表中的每个元素键的函数或属性名，返回的值作为key，key值要求唯一。
     * @param  {Object} context (optional) 如果传递了context参数，则把iterator绑定到context对象上
     * @return {Object} 返回一个指定了key的map
     */

    /**
     * 排序一个列表组成一个组，并且返回各组中的对象的数量的计数。
     * Counts instances of an object that group by a certain criterion. Pass either a string attribute to count by, or a function that returns the criterion.
     * @method countBy
     * <pre>
     ngc.countBy([1, 2, 3, 4, 5], function(num) {
          return num % 2 == 0 ? 'even': 'odd';
        });
     => {odd: 3, even: 2}
     * </pre>
     * @param  {Array} obj 一般为数组
     * @param  {Function} iterator (optional) 用来返回一个在列表中的每个元素键的函数或属性名，返回的值作为key。
     * @param  {Object} context (optional) 如果传递了context参数，则把iterator绑定到context对象上
     * @return {Object} 返回只有key值和对应key的数据总数的分组对象
     */

    /**
     * 使用二分查找确定value在list中的位置序号。
     * Use a comparator function to figure out the smallest index at which an object should be inserted so as to maintain order. Uses binary search.
     * @method sortedIndex
     * <pre>
     ngc.sortedIndex([10, 20, 30, 40, 50], 35);
     => 3
     var stooges = [{name: 'moe', age: 40}, {name: 'curly', age: 60}];
     ngc.sortedIndex(stooges, {name: 'larry', age: 50}, 'age');
     => 1
     * </pre>
     * @param  {Array} list 一般为数组
     * @param  {Object} obj  array排序后，定位obj插入的位置,使之能保持list原有的排序
     * @param  {Function} iterator (optional) 作为list排序的依据，也可以是字符串的属性名用来排序(比如length)
     * @param  {Object} context (optional) 如果传递了context参数，则把iterator绑定到context对象上
     * @return {Number} 返回obj所在的最终排序位置的下标。
     */
    /**
     * 把obj(任何可以迭代的对象)转换成一个数组，在转换 arguments 对象时非常有用。
     * Safely create a real, live array from anything iterable.
     * @method toArray
     * <pre>
     (function(){ return ngc.toArray(arguments).slice(1); })(1, 2, 3, 4);
     => [2, 3, 4]
     * </pre>
     * @param  obj 可以迭代的对象
     * @return {Array} 返回数组
     */
    /**
     * 返回对象的长度。如果对象没有length属性，返回属性的数量
     * Return the number of elements in an object.
     * @method size
     * <pre>
     ngc.size({one: 1, two: 2, three: 3});
     => 3
     * </pre>
     * @param  obj 可以迭代的对象
     * @return {Number} 返回对象的长度
     */
    /**
     * 返回数组的前n个元素。Aliased as `head` or `take`。
     * Get the first element of an array. Passing **n** will return the first N values in the array. Aliased as `head` and `take`. The **guard** check allows it to work with `ngc.map`.
     * @method first
     * <pre>
     ngc.first([5, 4, 3, 2, 1]);
     => 5
     * </pre>
     * @param  {Array} array 对象数组
     * @param  {Number} n (optional) 返回的前几个元素数量，为空默认为1.
     * @param  {Object} guard (optional) 设置为true会返回第一个元素
     * @return {Array} 如果第二参数未定义或者定义了第三个参数，则返回第一个元素，其他情况则返回n个随机元素的集合
     */
    /**
     * 排除数组的后n个元素。
     * Returns everything but the last entry of the array. Especially useful on the arguments object. Passing **n** will return all the values in the array, excluding the last N. The **guard** check allows it to work with `ngc.map`.
     * @method initial
     * <pre>
     ngc.initial([5, 4, 3, 2, 1]);
     => [5, 4, 3, 2]
     * </pre>
     * @param  {Array} array 对象数组
     * @param  {Number} n (optional) 排除的后几个元素数量，为空默认为1.
     * @param  {Object} guard (optional) 设置为true会返回第一个元素
     * @return {Array} 返回排除过的数据集合
     */
    /**
     * 返回数组里的后面的n个元素。
     * Get the last element of an array. Passing **n** will return the last N values in the array. The **guard** check allows it to work with `ngc.map`.
     * @method last
     * <pre>
     ngc.last([5, 4, 3, 2, 1]);
     => 1
     * </pre>
     * @param  {Array} array 对象数组
     * @param  {Number} n (optional) 返回的后几个元素数量，为空默认为1.
     * @param  {Object} guard (optional) 设置为true会返回最后一个元素
     * @return {Array} 如果第二参数未定义或者定义了第三个参数，则返回最后一个元素，其他情况则返回n个随机元素的集合
     */
    /**
     * 返回数组中除了第n个元素外的其他全部元素。Aliased as `tail` or `drop`。
     * Returns everything but the first entry of the array. Aliased as `tail` and `drop`.
     * Especially useful on the arguments object. Passing an **n** will return the rest N values in the array. The **guard** check allows it to work with `ngc.map`.
     * @method rest
     * <pre>
     ngc.rest([5, 4, 3, 2, 1]);
     => [4, 3, 2, 1]
     * </pre>
     * @param  {Array} array 对象数组
     * @param  {Number} n (optional) 从第几个元素后开始返回，为空默认为1.
     * @param  {Object} guard (optional) 设置为true会排除第一个元素
     * @return {Array} 返回排除了前几个元素的数据集合
     */
    /**
     * 返回一个除去所有false值的array副本。在javascript中, false, null, 0, "", undefined 和 NaN 都是false值.
     * Trim out all falsy values from an array.
     * @method compact
     * <pre>
     ngc.compact([0, 1, false, 2, '', 3]);
     => [1, 2, 3]
     * </pre>
     * @param  {Array} array 对象数组
     * @return {Array} 返回排除了值为false的数据集合
     */
    /**
     * 将一个嵌套多层的数组array(嵌套可以是任何层数)转换为只有一层的数组。
     * Flatten out an array, either recursively (by default), or just one level.
     * @method flatten
     * <pre>
     ngc.flatten([1, [2], [3, [[4]]]]);
     => [1, 2, 3, 4];
     ngc.flatten([1, [2], [3, [[4]]]], true);
     => [1, 2, 3, [[4]]];
     * </pre>
     * @param  {Array} array 对象数组
     * @param  {Object} shallow (optional) 如果传递shallow参数，数组将只减少一维的嵌套。
     * @return {Array} 返回嵌套只有一层的对象，或者减少一维嵌套。
     */
    /**
     * 返回一个删除了指定数据的array副本。剔除的数据为目标数组后面的参数，全等条件。
     * Return a version of the array that does not contain the specified value(s).
     * @method without
     * <pre>
     ngc.without([1, 2, 1, 0, 3, 1, 4], 0, 1);
     => [2, 3, 4]
     * </pre>
     * @param  {Array} array 目标数组
     * @return {Array} 返回剔除掉指定数据的数组
     */
    /**
     * 将一个数组按照一定的条件拆分成两个。
     * Split an array into two arrays: one whose elements all satisfy the given predicate, and one whose elements all do not satisfy the predicate.
     * @method partition
     * <pre>
     ngc.partition([1,2,3,4,5],function(item){return item >3 ;});
     => [[4,5],[1,2,3]]
     * </pre>
     * @param  {Array} array
     * @param  {Function} predicate 判断每一个元素，返回true还是false，根据返回值会将元素分到不同的数组里面
     * @param  {Object} context (optional) 如果传递了context参数，则把iterator绑定到context对象上
     * @return {Array} 返回一个长度为2的二维数组，一个数组内符合条件的数据，一个数组内为不符合条件的数据。
     */
    /**
     * 返回array去重后的副本, 使用全等条件.Aliased as `unique`。
     * Produce a duplicate-free version of the array. If the array has already been sorted, you have the option of using a faster algorithm.
     * Aliased as `unique`.
     * @method unique
     * <pre>
     ngc.uniq([1, 2, 1, 3, 1, 4]);
     => [1, 2, 3, 4]
     * </pre>
     * @param  {Array} array 对象数组
     * @param  {Boolean} isSorted (optional) 如果确定array已经排序, 那么给isSorted参数传递 true值, 此函数将运行的更快的算法。
     * @param  {Function} iterator (optional) 如果要处理对象元素, 传参iterator来获取要对比的属性.
     * @param  {Object} context (optional) 如果传递了context参数，则把iterator绑定到context对象上
     * @return {Array} 返回排过重的数组
     */
    /**
     * 返回传入的arrays（数组）并集：按顺序返回，数组的元素是唯一的，可以传入一个或多个 arrays（数组）
     * Produce an array that contains the union: each distinct element from all of the passed-in arrays.
     * @method union
     * <pre>
     ngc.union([1, 2, 3], [101, 2, 1, 10], [2, 1]);
     => [1, 2, 3, 101, 10]
     * </pre>
     * @return {Array} 合并了所有参数值的数组
     */
    /**
     * 返回传入arrays（数组）交集。结果中的每个值是存在于传入的每个arrays（数组）里。
     * Produce an array that contains every item shared between all the passed-in arrays.
     * @method intersection
     * <pre>
     ngc.intersection([1, 2, 3], [101, 2, 1, 10], [2, 1]);
     => [1, 2]
     * </pre>
     * @param  {Array} array 目标数组
     * @return {Array} 返回所有参数数组里面都包含的数值的集合
     */
    /**
     * 返回一个删除了指定数据的array副本。剔除的数据为参数数组中任一数组里面出现的元素。
     * Take the difference between one array and a number of other arrays.
     * Only the elements present in just the first array will remain.
     * @method difference
     * <pre>
     ngc.difference([1, 2, 3, 4, 5], [5, 2, 10]);
     => [1, 3, 4]
     * </pre>
     * @param  {Array} array 目标数组
     * @return {Array} 返回剔除掉指定数据的数组
     */
    /**
     * 将每个相应位置的arrays的值合并在一起。
     * Zip together multiple lists into a single array -- elements that share an index go together.
     * @method zip
     * <pre>
     ngc.zip(['moe', 'larry', 'curly'], [30, 40, 50], [true, false, false]);
     => [["moe", 30, true], ["larry", 40, false], ["curly", 50, false]]
     ngc.zip.apply(_, arrayOfRowsOfData);
     => arrayOfColumnsOfData
     * </pre>
     * @return {Array} 行转列之后的数组
     */
    /**
     * 将数组转换为对象。
     * Converts lists into objects. Pass either a single array of `[key, value]` pairs, or two parallel arrays of the same length -- one of keys, and one of the corresponding values.
     * @method object
     * <pre>
     ngc.object(['moe', 'larry', 'curly'], [30, 40, 50]);
     => {moe: 30, larry: 40, curly: 50}
     ngc.object([['moe', 30], ['larry', 40], ['curly', 50]]);
     => {moe: 30, larry: 40, curly: 50}
     * </pre>
     * @param  {Array} list 目标数组，可以是每一个元素为[key, value]格式的
     * @param  {Array} values (optional) 如果list参数是key的列表，此参数则为值的列表，长度和list一样
     * @return {Object}        返回一个对象
     */
    /**
     * 返回item在该 array 中的索引值，如果item不存在 array中就返回-1。使用原生的indexOf 函数，除非它失效。
     * If the browser doesn't supply us with indexOf (I'm looking at you, **MSIE**),we need this function. Return the position of the first occurrence of an item in an array, or -1 if the item is not included in the array.
     * Delegates to **ECMAScript 5**'s native `indexOf` if available.
     * If the array is large and already in sort order, pass `true` for **isSorted** to use binary search.
     * @method indexOf
     * <pre>
     ngc.indexOf([1, 2, 3], 2);
     => 1
     * </pre>
     * @param  {Array}  array     目标数组
     * @param  {Object}  item     数组内要寻找的目标元素
     * @param  {Boolean} isSorted (optional) 知道数组已经排序，传递true给isSorted将更快的用二进制搜索；也可以传入数字，表示将从你给定的索性值开始搜索。
     * @return {Number}           返回目标元素在数组中的索引值
     */
    /**
     * 返回item在该 array 中的从最后开始的索引值，如果item不存在 array中就返回-1。如果支持原生的lastIndexOf，将使用原生的lastIndexOf函数。
     * Delegates to **ECMAScript 5**'s native `lastIndexOf` if available.
     * @method lastIndexOf
     * <pre>
     ngc.lastIndexOf([1, 2, 3, 1, 2, 3], 2);
     => 4
     * </pre>
     * @param  {Array}  array     目标数组
     * @param  {Object}  item     数组内要寻找的目标元素
     * @param  {Number} from (optional) 将从你给定的索性值开始搜索。
     * @return {Number}           返回目标元素在数组中的索引值
     */
    /**
     * 一个用来创建整数灵活编号的列表的函数，便于each 和 map循环。
     * Generate an integer Array containing an arithmetic progression. A port of the native Python `range()` function. See [the Python documentation](http://docs.python.org/library/functions.html#range).
     * @method range
     * <pre>
     ngc.range(10);
     => [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
     ngc.range(1, 11);
     => [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
     ngc.range(0, 30, 5);
     => [0, 5, 10, 15, 20, 25]
     ngc.range(0, -10, -1);
     => [0, -1, -2, -3, -4, -5, -6, -7, -8, -9]
     ngc.range(0);
     => []
     * </pre>
     * @param  {Number} start (optional) 默认为 0
     * @param  {Number} stop (optional)  决定返回的数据数量
     * @param  {Number} step (optional) 默认为 1,可设置为负数表示负增长
     * @return {Array}       返回一个从start 到stop的整数的列表
     */
    /**
     * 绑定函数 function 到对象 object 上
     * Create a function bound to a given object (assigning `this`, and arguments, optionally).
     * Delegates to **ECMAScript 5**'s native `Function.bind` if available.
     * @method bind
     * <pre>
     var func = function(greeting){ return greeting + ': ' + this.name };
     func = _.bind(func, {name: 'moe'}, 'hi');
     func();
     => 'hi: moe'
     * </pre>
     * @param  {Function} function  要执行的方法
     * @param  {Object} object 方法的执行者，对应func函数内的this
     * @param {Object} arguments (optional) 任意可选参数arguments 可以绑定到函数 function , 可以填充函数所需要的参数
     * @return {Object}         func执行的返回值
     */
    /**
     * 局部应用一个函数填充在任意数值的参数， 不改变其动态this值
     * Partially apply a function by creating a version that has had some of its arguments pre-filled, without changing its dynamic `this` context. _ acts as a placeholder, allowing any combination of arguments to be pre-filled.
     * @method partial
     * <pre>
     var add = function(a, b) { return a + b; };
     add5 = ngc.partial(add, 5);
     add5(10);
     => 15
     * </pre>
     * @param  {Function} func  要执行的方法
     * @param {Object} arguments (optional) 任意可选参数arguments可以绑定到函数function,可以填充函数所需要的参数
     * @return {Object}         func执行的返回值
     */
    /**
     * 把methodNames参数指定的方法绑定到object上，这些方法就会在对象的上下文环境中执行。绑定函数用作事件处理函数时非常便利，否则函数被调用时this一点用也没有。
     * Bind a number of an object's methods to that object. Remaining arguments are the method names to be bound. Useful for ensuring that all callbacks defined on an object belong to it.
     * @method bindAll
     * <pre>
     var buttonView = {
          label  : 'underscore',
          onClick: function(){ alert('clicked: ' + this.label); },
          onHover: function(){ console.log('hovering: ' + this.label); }
        };
     ngc.bindAll(buttonView, 'onClick', 'onHover');
     // When the button is clicked, this.label will have the correct value.
     jQuery('#underscore_button').bind('click', buttonView.onClick);
     * </pre>
     * @param  {Object} object 目标对象
     * @param  {String} methodNames (optional) 如果不设置methodNames参数，对象上的所有方法都会被绑定。
     * @return {Object}     绑定后的目标对象
     */
    /**
     * Memoizes方法可以缓存某函数的计算结果。对于耗时较长的计算是很有帮助的。默认使用function的第一个参数作为key
     * Memoize an expensive function by storing its results.
     * @method memoize
     * <pre>
     var fibonacci = ngc.memoize(function(n) {
          return n < 2 ? n: fibonacci(n - 1) + fibonacci(n - 2);
        });
     * </pre>
     * @param  {Function} func  要执行的方法
     * @param  {Function} hasher (optional) 的返回值作为key存储函数的计算结果。
     * @return {Object}        返回缓存的结果
     */
    /**
     * 类似setTimeout，等待wait毫秒后调用function。
     * Delays a function for the given number of milliseconds, and then calls it with the arguments supplied.
     * @method delay
     * <pre>
     var log = ngc.bind(console.log, console);
     ngc.delay(log, 1000, 'logged later');
     => 'logged later' // Appears after one second.
     * </pre>
     * @param  {Function} function  要执行的方法
     * @param  {Number} wait 延迟执行的时间
     * @param  {Object} arguments (optional) 如果传递可选的参数arguments，当函数func执行时， arguments 会作为参数传入。
     * @return {Object}      返回执行的结果
     */
    /**
     * 延迟调用function直到当前调用栈清空为止，类似使用延时为1的setTimeout方法。对于执行开销大的计算和无阻塞UI线程的HTML渲染时候非常有用。
     * Defers a function, scheduling it to run after the current call stack has cleared.
     * @method defer
     * <pre>
     ngc.defer(function(){ alert('deferred'); });
     // Returns from the function before the alert runs.
     * </pre>
     * @param  {Function} func  要执行的方法
     * @param  {Object} arguments (optional) 如果传递可选的参数arguments，当函数func执行时，arguments 会作为参数传入。
     * @return {Object}      返回执行的结果
     */
    /**
     * 创建并返回一个像节流阀一样的函数，当重复调用函数的时候，最多每隔 wait毫秒调用一次该函数。 对于想控制一些触发频率较高的事件有帮助。
     * Returns a function, that, when invoked, will only be triggered at most once during a given window of time. Normally, the throttled function will run as much as it can, without ever going more than once per `wait` duration;
     * but if you'd like to disable the execution on the leading edge, pass `{leading: false}`. To disable execution on the trailing edge, ditto.
     * @method throttle
     * <pre>
     var throttled = ngc.throttle(updatePosition, 100);
     $(window).scroll(throttled);
     * </pre>
     * @param  {Function} func  要执行的方法
     * @param  {Number} wait    函数调用的间隔时间
     * @param  {Object} options (optional) 禁用第一次首先执行的话，传递{leading: false}，禁用最后一次执行的话，传递{trailing: false}。
     * @return {Object}      返回执行的结果
     */
    /**
     * 返回 function 函数的防反跳版本, 将延迟函数的执行(真正的执行)在函数最后一次调用时刻的 wait 毫秒之后. 对于必须在一些输入（多是一些用户操作）停止到达之后执行的行为有帮助。
     * Returns a function, that, as long as it continues to be invoked, will not be triggered. The function will be called after it stops being called for N milliseconds. If `immediate` is passed, trigger the function on the leading edge, instead of the trailing.
     * @method debounce
     * <pre>
     var lazyLayout = ngc.debounce(calculateLayout, 300);
     $(window).resize(lazyLayout);
     * </pre>
     * @param  {Function} func  要执行的方法
     * @param  {Number} wait    函数调用的延时时间
     * @param  {Boolean} immediate (optional) 传参 immediate 为 true 会让 debounce 在 wait 间隔之后 触发最后的函数调用而不是最先的函数调用. 在类似不小心点了提交按钮两下而提交了两次的情况下很有用.
     * @return {Object}      返回执行的结果
     */
    /**
     * 创建一个只能调用一次的函数。重复调用改进的方法也没有效果，只会返回第一次执行时的结果。 作为初始化函数使用时非常有用, 不用再设一个boolean值来检查是否已经初始化完成.
     * Returns a function that will be executed at most one time, no matter how often you call it. Useful for lazy initialization.
     * @method once
     * <pre>
     var initialize = _.once(createApplication);
     initialize();
     initialize();
     // Application is only created once.
     * </pre>
     * @param  {Function} func  要执行的方法
     * @return {Object}      返回执行的结果
     */
    /**
     * 将第一个函数 function 封装到函数 wrapper 里面, 并把函数 function 作为第一个参数传给 wrapper. 这样可以让 wrapper 在 function 运行之前和之后 执行代码, 调整参数然后附有条件地执行.
     * Returns the first function passed as an argument to the second,allowing you to adjust arguments, run code before and after, and conditionally execute the original function.
     * @method wrap
     * <pre>
     var hello = function(name) { return "hello: " + name; };
     hello = ngc.wrap(hello, function(func) {
          return "before, " + func("moe") + ", after";
        });
     hello();
     => 'before, hello: moe, after'
     * </pre>
     * @param  {Function} func  要执行的方法
     * @param  {Function} wrapper 外围方法，func将会作为此方法的第一个参数，可以在这个方法里面有条件的调用func
     * @return {Object}      返回执行的结果
     */
    /**
     * 返回函数集 functions 组合后的复合函数, 也就是一个函数执行完之后把返回的结果再作为参数赋给下一个函数来执行. 以此类推. 在数学里, 把函数 f(), g(), 和 h() 组合起来可以得到复合函数 f(g(h())).
     * Returns a function that is the composition of a list of functions, each consuming the return value of the function that follows.
     * @method compose
     * <pre>
     var greet    = function(name){ return "hi: " + name; };
     var exclaim  = function(statement){ return statement.toUpperCase() + "!"; };
     var welcome = ngc.compose(greet, exclaim);
     welcome('moe');
     => 'hi: MOE!
     * </pre>
     * @param  {Function} func (optional) 任何个参数func将会被嵌套进来执行，后一个func的返回值作为前一个func的参数
     * @return {Object}      返回执行的结果
     */
    /**
     * 创建一个函数, 只有在运行了times次之后才有效果. 在处理同组异步请求返回结果时, 如果你要确保同组里所有异步请求完成之后才 执行这个函数, 这将非常有用.
     * Returns a function that will only be executed after being called N times.
     * @method after
     * <pre>
     var renderNotes = ngc.after(notes.length, render);
     ngc.each(notes, function(note) {
          note.asyncSave({success: renderNotes});
        });
     // renderNotes is run once, after all notes have saved.
     * </pre>
     * @param  {Number} times 函数执行的次数
     * @param  {Function} func  函数执行次数到达之后真正触发的事件
     * @return {Object}      返回执行的结果
     */
    /**
     * 获取object对象所有的属性名称。
     * Retrieve the names of an object's properties.
     * Delegates to **ECMAScript 5**'s native `Object.keys`
     * @method keys
     * <pre>
     ngc.keys({one: 1, two: 2, three: 3});
     => ["one", "two", "three"]
     * </pre>
     * @param  {Object} obj 目标对象
     * @return {Array}     目标对象的属性名称
     */
    /**
     * 返回object对象所有的属性值。
     * Retrieve the values of an object's properties.
     * @method values
     * <pre>
     ngc.values({one: 1, two: 2, three: 3});
     => [1, 2, 3]
     * </pre>
     * @param  {Object} obj 目标对象
     * @return {Array}     目标对象的属性值
     */
    /**
     * 把一个对象转变为一个[key, value]形式的数组。
     * Convert an object into a list of `[key, value]` pairs.
     * @method pairs
     * <pre>
     ngc.pairs({one: 1, two: 2, three: 3});
     => [["one", 1], ["two", 2], ["three", 3]]
     * </pre>
     * @param  {Object} obj 目标对象
     * @return {Array}      返回数组，每个元素都是属性名和属性值组成。
     */
    /**
     * 返回一个object副本，使其键（keys）和值（values）对换。对于这个操作，必须确保object里所有的值都是唯一的且可以序列号成字符串.
     * Invert the keys and values of an object. The values must be serializable.
     * @method invert
     * <pre>
     ngc.invert({Moe: "Moses", Larry: "Louis", Curly: "Jerome"});
     => {Moses: "Moe", Louis: "Larry", Jerome: "Curly"};
     * </pre>
     * @param  {Object} object 目标对象
     * @return {Object}      转换过属性名和属性值的对象。
     */

    /**
     * 返回一个对象里所有的方法名, 而且是已经排序的 。Aliased as `methods`。
     * Return a sorted list of the function names available on the object.
     * Aliased as `methods`
     * @method functions
     * <pre>
     ngc.functions(_);
     => ["all", "any", "bind", "bindAll", "clone", "compact", "compose" ...
     * </pre>
     * @param  {Object} obj 目标对象
     * @return {Array}      返回数组，每个元素是属性值为函数的属性名称。
     */

    /**
     * 复制source对象中的所有属性覆盖到destination对象上，并且返回 destination 对象. 复制是按顺序的, 所以后面的对象属性会把前面的对象属性覆盖掉(如果有重复).
     * Extend a given object with all the properties in passed-in object(s).
     * @method extend
     * <pre>
     ngc.extend({name: 'moe'}, {age: 50});
     => {name: 'moe', age: 50}
     * </pre>
     * @param  {Object} destination 目标参数
     * @param  {Object} source (optional) 一些列的复制对象，最终这些对象的属性都附加到第一个对象上
     * @return {Object} 返回第一个对象(目标对象)
     */

    /**
     * 返回一个object副本，只过滤出keys(有效的键组成的数组)参数指定的属性值。
     * Return a copy of the object only containing the whitelisted properties.
     * @method pick
     * <pre>
     ngc.pick({name: 'moe', age: 50, userid: 'moe1'}, 'name', 'age');
     => {name: 'moe', age: 50}
     * </pre>
     * @param  {Object} object 目标对象
     * @param  {Object} keys (optional) 一些列的属性key，最终object对象的这些属性都会被提炼到返回副本里面
     * @return {Object}     从目标对象中返回指定keys的副本对象
     */

    /**
     * 返回一个object副本，只过滤出除去keys(有效的键组成的数组)参数指定的属性值。
     * Return a copy of the object without the blacklisted properties.
     * @method omit
     * <pre>
     ngc.omit({name: 'moe', age: 50, userid: 'moe1'}, 'userid');
     => {name: 'moe', age: 50}
     * </pre>
     * @param  {Object} object 目标对象
     * @param  {Object} keys (optional) 一些列的属性key，最终object对象的这些属性都会在返回的副本对象里面剔除
     * @return {Object}     从目标对象中返回不含指定keys的副本对象
     */

    /**
     * 用defaults对象填充object中undefined属性。并且返回这个object。一旦这个属性被填充，再使用defaults方法将不会有任何效果。
     * Fill in a given object with default properties.
     * @method defaults
     * <pre>
     var iceCream = {flavor: "chocolate"};
     ngc.defaults(iceCream, {flavor: "vanilla", sprinkles: "lots"});
     => {flavor: "chocolate", sprinkles: "lots"}
     * </pre>
     * @param  {Object} object 目标对象
     * @param  {Object} defaults (optional) 一些列的复制对象，会按顺序将obj中没有的属性加上
     * @return {Object}     返回一个被填充后的对象
     */

    /**
     * 创建 一个浅复制（浅拷贝）的克隆object。任何嵌套的对象或数组都通过引用拷贝，不会复制。
     * Create a (shallow-cloned) duplicate of an object.
     * @method clone
     * <pre>
     ngc.clone({name: 'moe'});
     => {name: 'moe'};
     * </pre>
     * @param  {Object} object 目标对象
     * @return {Object}     目标对象的副本
     */

    /**
     * 用 object作为参数来调用函数interceptor，然后返回object。这种方法的主要意图是作为函数链式调用 的一环, 为了对此对象执行操作并返回对象本身。
     * Invokes interceptor with the obj, and then returns obj.
     * The primary purpose of this method is to "tap into" a method chain, in order to perform operations on intermediate results within the chain.
     * @method tap
     * <pre>
     ngc.chain([1,2,3,200])
     .filter(function(num) { return num % 2 == 0; })
     .tap(alert)
     .map(function(num) { return num * num })
     .value();
     => // [2, 200] (alerted)
     => [4, 40000]
     * </pre>
     * @param  {Object} object
     * @param  {Function} interceptor
     * @return {Object}  object
     */

    /**
     * 执行两个对象之间的优化深度比较，确定他们是否应被视为相等。
     * Perform a deep comparison to check if two objects are equal.
     * @method isEqual
     * <pre>
     var moe   = {name: 'moe', luckyNumbers: [13, 27, 34]};
     var clone = {name: 'moe', luckyNumbers: [13, 27, 34]};
     moe == clone;
     => false
     ngc.isEqual(moe, clone);
     => true
     * </pre>
     * @param  {Object}  a 源对象
     * @param  {Object}  b 目标对象
     * @return {Boolean}   两对象值是否相等
     */
    /**
     * 如果object 不包含任何值(没有可枚举的属性)，返回true。也可以判断字符串或者数组的长度为0
     * Is a given array, string, or object empty?
     * An "empty" object has no enumerable own-properties.
     * @method isEmpty
     * <pre>
     ngc.isEmpty([1, 2, 3]);
     => false
     ngc.isEmpty({});
     => true
     * </pre>
     * @param  {Object}  object 目标对象
     * @return {Boolean}    目标对象是否为空值
     */
    /**
     * 如果object是一个DOM元素，返回true。属性nodeType为1表示为element对象
     * Is a given value a DOM element?
     * @method isElement
     * <pre>
     ngc.isElement(jQuery('body')[0]);
     => true
     * </pre>
     * @param  {Object}  object 目标对象
     * @return {Boolean} 是否为dom对象
     */
    /**
     * 如果object是一个数组，返回true。如果存在原生的Array.isArray方法，就用原生方法来代替。
     * Is a given value an array?
     * Delegates to ECMA5's native Array.isArray
     * @method isArray
     * <pre>
     (function(){ return ngc.isArray(arguments); })();
     => false
     ngc.isArray([1,2,3]);
     => true
     * </pre>
     * @param  {Object}  object 目标对象
     * @return {Boolean} 目标对象是否为数组
     */
    /**
     * 如果object是一个对象，返回true。需要注意的是JavaScript数组和函数是对象，字符串和数字不是。
     * Is a given variable an object?
     * @method isObject
     * <pre>
     ngc.isObject({});
     => true
     ngc.isObject(1);
     => false
     * </pre>
     * @param  {Object}  object 目标对象
     * @return {Boolean}     true or false
     */
    /**
     * 如果object是一个参数对象，返回true
     * Is a given variable an arguments?
     * @method isArguments
     * <pre>
     (function(){ return ngc.isArguments(arguments); })(1, 2, 3);
     => true
     ngc.isArguments([1,2,3]);
     => false
     * </pre>
     * @param {Object}  object 目标对象
     * @return {Boolean}    true or false
     */
    /**
     * 如果object是一个函数对象，返回true
     * Is a given variable a function?
     * @method isFunction
     * <pre>
     ngc.isFunction(alert);
     => true
     * </pre>
     * @param {Object}  object 目标对象
     * @return {Boolean}    true or false
     */
    /**
     * 如果object是一个字符串，返回true
     * Is a given variable a String?
     * @method isString
     * <pre>
     ngc.isString("moe");
     => true
     * </pre>
     * @param {Object}  object 目标对象
     * @return {Boolean}    true or false
     */
    /**
     * 如果object是一个数值，返回true, 包括 NaN
     * Is a given variable a number?
     * @method isNumber
     * <pre>
     ngc.isNumber(8.4 * 5);
     => true
     * </pre>
     * @param {Object}  object 目标对象
     * @return {Boolean}    true or false
     */
    /**
     * 如果object是一个日期时间，返回true
     * Is a given variable a date?
     * @method isDate
     * <pre>
     ngc.isDate(new Date());
     => true
     * </pre>
     * @param {Object}  object 目标对象
     * @return {Boolean}    true or false
     */
    /**
     * 如果object是一个正则表达式，返回true
     * Is a given variable a regExp?
     * @method isRegExp
     * <pre>
     ngc.isRegExp(/moe/);
     => true
     * </pre>
     * @param {Object}  object 目标对象
     * @return {Boolean}    true or false
     */
    /**
     * 如果object是一个有限的数字，返回true。
     * Is a given object a finite number?
     * @method isFinite
     * <pre>
     ngc.isFinite(-101);
     => true
     ngc.isFinite(-Infinity);
     => false
     * </pre>
     * @param  {Object}  object 目标对象
     * @return {Boolean}     true or false
     */
    /**
     * 如果object是数组或者NaN，返回true。和原生的isNaN 函数不一样，如果变量是undefined，原生的isNaN 函数也会返回 true
     * Is the given value `NaN`? (NaN is the only number which does not equal itself).
     * @method isNaN
     * <pre>
     ngc.isNaN(NaN);
     => true
     isNaN(undefined);
     => true
     ngc.isNaN(undefined);
     => false
     * </pre>
     * @param  {Object}  object 目标对象
     * @return {Boolean}     true or false
     */
    /**
     * 如果object是一个布尔值，返回true。
     * Is a given value a boolean?
     * @method isBoolean
     * <pre>
     ngc.isBoolean(null);
     => false
     * </pre>
     * @param  {Object}  object 目标对象
     * @return {Boolean}     true or false
     */
    /**
     * 如果object的值是 null，返回true。
     * Is a given value equal to null?
     * @method isNull
     * <pre>
     ngc.isNull(null);
     => true
     ngc.isNull(undefined);
     => false
     * </pre>
     * @param  {Object}  object 目标对象
     * @return {Boolean}     true or false
     */

    /**
     * 如果value是undefined，返回true。
     * Is a given variable undefined?
     * @method isUndefined
     * <pre>
     ngc.isUndefined(window.missingVariable);
     => true
     * </pre>
     * @param  {Object}  value 目标对象
     * @return {Boolean}     true or false
     */

    /**
     * 对象是否包含给定的键吗？等同于object.hasOwnProperty(key)，但是使用hasOwnProperty 函数的一个安全引用，以防意外覆盖。
     * Shortcut function for checking if an object has a given property directly on itself (in other words, not on a prototype).
     * @method has
     * <pre>
     ngc.has({a: 1, b: 2, c: 3}, "b");
     => true
     * </pre>
     * @param  {Object} obj 目标对象
     * @param  {String}  key 目标的属性名称
     * @return {Boolean}     对象是否拥有属性true or false
     */

    /**
     * 返回与传入参数相等的值. 相当于数学里的: f(x) = x, 在Underscore里被用作默认的迭代器iterator.
     * Keep the identity function around for default iterators.
     * @method identity
     * <pre>
     var moe = {name: 'moe'};
     moe === ngc.identity(moe);
     => true
     * </pre>
     * @param  {Object} value 传入的值
     * @return {Object} 返回传入的值
     */

    /**
     * 返回一个函数，函数的返回值就是传入的参数。 相当于x=f(x)();
     * @method constant
     * <pre>
     var obj={a:11};
     var func=ngc.constant(a);
     func().a
     => 11
     * </pre>
     * @param  {Object} value 传入的值
     * @return {Function}     返回无参函数，函数返回值为传入的值。
     */

    /**
     * 返回一个函数，在函数的参数对象里面取key对应的值
     * Returns a function that will itself return the key property of any passed-in object.
     * @method property
     * <pre>
     var moe = {name: 'moe'};
     'moe' === ngc.property('name')(moe);
     => true
     * </pre>
     * @param  {String} key 属性名称
     * @return {Function}   返回函数，函数内会取参数对象的属性名用来返回属性值
     */

    /**
     * 返回一个函数，若函数的参数如果都具有attr中一样的属性，则返回true。
     * Returns a predicate function that will tell you if a passed in object contains all of the key/value properties present in attrs.
     * @method mathes
     * <pre>
     var ready = ngc.matches({selected: true, visible: true});
     var readyToGoList = ngc.filter(list, ready);
     * </pre>
     * @param  {Object} attr  需要判断的属性对象
     * @return {Function}      返回函数，函数内会判断参数对象是否都包含attr的属性值，返回true or false
     */

    /**
     * 调用给定的迭代函数n次,每一次传递index参数，调用迭代函数。 注意: 本例使用 链式语法。
     * Run a function **n** times.
     * @method times
     * <pre>
     _(3).times(function(n){ genie.grantWishNumber(n); });
     => alert(n) 分别打印出为0,1,2
     * </pre>
     * @param  {Number} n        函数调用的次数，每调用一次，index加1，index从0开始
     * @param  {Function} iterator 执行函数
     * @param  {Object} context (optional) 如果传递了context参数，则把iterator绑定到context对象上
     * @return {Array}          返回一个长度为n的数组，每个值为每次调用函数的返回值
     */

    /**
     * 返回一个min 和 max之间的随机整数。
     * Return a random integer between min and max (inclusive).
     * @method random
     * <pre>
     ngc.random(0, 100);
     => 42
     * </pre>
     * @param  {Number} min (optional) 返回的随机整数的最小范围，此参数不传，默认为0，
     * @param  {Number} max (optional) 返回的随机整数的最大范围
     * @return {Number}     返回一个范围内的整数
     */

    /**
     * 返回当前时间的时间戳
     * A (possibly faster) way to get the current timestamp as an integer.
     * @method now
     * <pre>
     ngc.now();
     => 1393573920327
     * </pre>
     * @return {Number}     返回时间戳
     */

    /**
     * 转义HTML字符串，替换&, <, >, ", ', and /字符。
     * Functions for escaping strings from HTML interpolation.
     * @method escape
     * <pre>
     ngc.escape('Curly, Larry & Moe');
     => "Curly, Larry &amp; Moe"
     * </pre>
     * @param {String}  string 目标字符串
     * @return {String}        转义后的字符串
     */
    /**
     * 和**{@link ngc.component.ngc#escape escape}**相反。转义HTML字符串，替换&, &lt;, &gt;, &quot;, &#x27;, and &#x2F;字符。
     * Functions for unescaping strings to HTML interpolation.
     * @method unescape
     * <pre>
     ngc.unescape('Curly, Larry &amp; Moe');
     => "Curly, Larry & Moe"
     * </pre>
     * @param {String}  string 目标字符串
     * @return {String}        还原转义后的字符串，
     */
    /**
     * 如果对象 object 中的属性 property 是函数, 则调用它, 否则, 返回它。
     * If the value of the named `property` is a function then invoke it with the `object` as context; otherwise, return it.
     * @method result
     * <pre>
     var object = {cheese: 'crumpets', stuff: function(){ return 'nonsense'; }};
     ngc.result(object, 'cheese');
     => "crumpets"
     ngc.result(object, 'stuff');
     => "nonsense"
     * </pre>
     * @param  {Object} object   目标对象
     * @param  {String} property 属性名
     * @return {Object}          返回属性对应的值，或者属性对应的方法执行的返回值
     */
    /**
     * 您可以用您自己的实用程序函数扩展Underscore。传递一个 {name: function}定义的哈希添加到Underscore对象，以及面向对象封装。
     * Add your own custom functions to the Underscore object.
     * @method mixin
     * <pre>
     ngc.mixin({
          capitalize: function(string) {
            return string.charAt(0).toUpperCase() + string.substring(1).toLowerCase();
          }
        });
     _("fabio").capitalize();
     => "Fabio"
     * </pre>
     * @param  {Object} obj 定义为{name: function}的对象附加到Underscore原型链上，支持Underscore对象之间调用name()方法来执行function
     * @return {Object}     将方法定义到Underscore原型链上
     */
    /**
     * 为需要的客户端模型或DOM元素生成一个全局唯一的id。
     * Generate a unique integer id (unique within the entire client session).
     * Useful for temporary DOM ids.
     * @method uniqueId
     * <pre>
     ngc.uniqueId('contact_');
     => 'contact_104'
     * </pre>
     * @param  {String} prefix (optional) 如果prefix参数存在， id 将附加给它。
     * @return {String}        生成全局唯一的字符串
     */

    /**
     * 对一个对象使用 chain 方法, 会把这个对象封装并 让以后每次方法的调用结束后都返回这个封装的对象.支持链式语法
     * Add a "chain" function, which will delegate to the wrapper.
     * @method chain
     * <pre>
     var stooges = [{name: 'curly', age: 25}, {name: 'moe', age: 21}, {name: 'larry', age: 23}];
     var youngest = ngc.chain(stooges)
     .sortBy(function(stooge){ return stooge.age; })
     .map(function(stooge){ return stooge.name + ' is ' + stooge.age; })
     .first()
     .value();
     => "moe is 21"
     * </pre>
     * @param  {Object} obj 目标对象
     * @return {Object}     返回链式对象
     */
    /**
     * 获取封装对象的最终值.
     * Extracts the result from a wrapped and chained object.
     * @method value
     * <pre>
     _([1, 2, 3]).value();
     => [1, 2, 3]
     * </pre>
     * @return {Object} 返回封装对象的原始值
     */


	return ngc;
});
